{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `record` 关键字\n",
    "\n",
    "`record` 关键字用于声明一个不可变的类，类似于 `struct` 关键字。与 `struct` 不同，`record` 关键字声明的类具有以下特点：\n",
    "\n",
    "- 默认情况下，`record` 类是不可继承的（`sealed`）。\n",
    "- 默认情况下，`record` 类是只读的（`readonly`）。\n",
    "- 默认情况下，`record` 类具有自动实现的属性（`auto-implemented properties`）。\n",
    "\n",
    "这里 `record` 配合主构造函数使用，相当于创建了名为`Name`的 `{ get; init;}` 属性。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "不完全是这样。`record`关键字在 C#中用来定义一个记录类型，它是一种引用类型，并且默认提供一些特性，比如结构相等性（基于它的成员进行比较）和不可变性。但是，`record`类型中的属性并不一定都是`{ get; init; }`的。\n",
    "\n",
    "在定义`record`时，你可以直接在属性声明中使用`init`关键字，这样定义的属性只能在对象构造时被初始化，并且之后不能被修改。例如：\n",
    "\n",
    "```csharp\n",
    "public record Person(string FirstName, string LastName, int Age);\n",
    "```\n",
    "\n",
    "在这个例子中，`FirstName`、`LastName`和`Age`属性都是只读的，并且只能在创建`Person`实例时初始化。\n",
    "\n",
    "但是，如果你需要一个属性是可变的，你可以像定义普通类一样为属性添加`set`访问器：\n",
    "\n",
    "```csharp\n",
    "public record Person(string FirstName, string LastName, int Age)\n",
    "{\n",
    "    public string MiddleName { get; set; }\n",
    "}\n",
    "```\n",
    "\n",
    "在这个例子中，`MiddleName`属性是可变的，因为它具有`set`访问器，允许你在任何时候修改它的值。\n",
    "\n",
    "总结一下：\n",
    "\n",
    "- 使用`record`关键字定义的类型默认具有结构相等性，这意味着它们是基于内容而不是引用进行比较的。\n",
    "- `record`类型中的属性，如果是在类型声明时直接初始化的（例如`public record Person(string FirstName, string LastName)`），则默认是不可变的，相当于`{ get; init; }`。\n",
    "- 你仍然可以在`record`中定义具有`set`访问器的属性，这些属性是可变的。\n",
    "\n",
    "因此，`record`关键字创建的属性并不一定都是`{ get; init; }`的；这取决于你是否为属性提供了`set`访问器。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [],
   "source": [
    "#load \"..\\Extension\\LoadBaseTool.csx\"\n",
    "using BaseTool;\n",
    "using System;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [],
   "source": [
    "public record Person(string Name, int Age)\n",
    "{\n",
    "    public int ValueReadOnly  { get;} = Age; \n",
    "    public int ValueInitOnly  { get; init;} \n",
    "    public int ValueCanSet  { get; set;} \n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Person { Name = asd, Age = 10, ValueReadOnly = 10, ValueInitOnly = 101, ValueCanSet = 102 }\n",
      "Person { Name = que, Age = 20, ValueReadOnly = 10, ValueInitOnly = 301, ValueCanSet = 300 }\n"
     ]
    }
   ],
   "source": [
    "Person p1 = new Person(\"asd\", 10) \n",
    "{\n",
    "    ValueCanSet = 100,\n",
    "    ValueInitOnly = 101,\n",
    "};\n",
    "Person p2 = p1 with { Name = \"que\", Age = 20, ValueCanSet = 300, ValueInitOnly = 301, };\n",
    "\n",
    "// p1.Name = \"QQ\";  // 报错\n",
    "// p1.ValueReadOnly = 100;  // 报错\n",
    "// p1.ValueInitOnly = 101;  // 报错\n",
    "p1.ValueCanSet = 102;\n",
    "\n",
    "Console.WriteLine(p1);\n",
    "Console.WriteLine(p2); // 改了Age之后，ValueReadOnly和Age不再相等"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意这里的对比的区别\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "True\n",
      "Person { Name = que, Age = 20, ValueReadOnly = 10, ValueInitOnly = 301, ValueCanSet = 300 }\n",
      "Person { Name = que, Age = 20, ValueReadOnly = 20, ValueInitOnly = 301, ValueCanSet = 300 }\n",
      "Person { Name = que, Age = 20, ValueReadOnly = 10, ValueInitOnly = 301, ValueCanSet = 300 }\n"
     ]
    }
   ],
   "source": [
    "Person p3 = new Person(\"que\", 20) { ValueCanSet = 300, ValueInitOnly = 301 };\n",
    "(p2 == p3).Dump();  // ValueReadOnly和Age不再相等\n",
    "\n",
    "Person p4 = p1 with { Name = \"que\", Age = 20, ValueCanSet = 300, ValueInitOnly = 301, };\n",
    "(p2 == p4).Dump();\n",
    "\n",
    "Console.WriteLine(p2);\n",
    "Console.WriteLine(p3);\n",
    "Console.WriteLine(p4);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "两者内容相同则相等\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\r\n"
     ]
    }
   ],
   "source": [
    "Person p5 = new Person(\"asd\", 10) { ValueCanSet = 102, ValueInitOnly = 101 };\n",
    "Person p6 = new Person(\"asd\", 10) { ValueCanSet = 102, ValueInitOnly = 101 };\n",
    "(p6 == p5).Dump();"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "name": "polyglot-notebook"
  },
  "polyglot_notebook": {
   "kernelInfo": {
    "defaultKernelName": "csharp",
    "items": [
     {
      "aliases": [],
      "languageName": "csharp",
      "name": "csharp"
     }
    ]
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
